﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

/**
 * \file CollidableObject.cs
 */

/**
 * The base type of every object in the game.
 * objects of this type are involved in game updates
 * and have a 'collision' cube defined.
 */
public class CollidableObject
{
    /** whether or not this object can be culled.*/
    protected Boolean cull = false;

    /**whether or not to display my debug points */
    protected Boolean debug = Game.debug;

    /** 
     * public read-only variable so other methods
     * can see whether or not this instance is
     * allowed to be culled.
     */
    public Boolean isCullable
    {
        get { return cull; }
    }

    /** the x,y,z position of this object */
    protected Vector3 origin;

    /**
     * public read-only variable so other methods
     * can get the position of this instance.
     */
    public Vector3 position
    {
        get { return origin; }
        set { origin = value; }
    }

    /** the maximum x,y,z position of this objects
     * collision cube. defines the back, bottom, right corner
     */
    protected Vector3 max;

    /** the minimum x,y,z position of this objects
     * collision cube. defines the forward, top, left corner
     */
    protected Vector3 min;

    /**
     * read-only, the x,y,z coordinates of the
     * bottom, right, back corner of the collision box
     */
    public Vector3 collisionMax
    {
        get { return max; }
    }

    /**
     * read-only, the x,y,z coordinates of the
     * top, left, front corner of the collision box
     */
    public Vector3 collisionMin
    {
        get { return min; }
    }

    /** specifies exactly what type of CollisionObject
     * this instance is.
     */
    protected CollidableObject.Type thisType;
    public CollidableObject.Type myType
    {
        get { return thisType; }
    }

    /** the SpawnPointer that designates this instance (optional).
     * When this object culls itself from screen it will
     * reset the SpawnPoint.
     */
    protected SpawnPoint spawner;

    /**
     * creates a CollidableObject at 'placement' of type 'type'.
     * initializes origin, and
     * type. This will set the SpawnPointer for this instance
     * to null.
     * @param type an instance of ObjectFactory.TypeEnum which
     * specifies what type of CollidableObject to create
     * @param placement the world x,y,z coordinates that define
     * where this instance is placed.
     * @see ObjectFactory.TypeEnum
     */
    public CollidableObject(CollidableObject.Type type, Vector3 placement)
    {
        init(type, placement);
    }

    /**
     * does all the necessary initialization processing for this instance.
     */
    protected void init(CollidableObject.Type type, Vector3 center)
    {
        thisType = type;
        origin = center;
        min = origin - thisType.collisionBoxOriginOffset;
        max = min + thisType.collisionBoxDimentions;
    }

    /**
     * sets the SpawnPoint for this object so it can
     * be reset when this is culled.
     */
    public void setSpawnPoint(SpawnPoint spawnPoint)
    {
        spawner = spawnPoint;
    }

    /**
     * does necessary update processing.
     * Checks to see if the object's bounding cube
     * is visible by the camera. if not, this
     * object will be flagged to be removed from
     * the update list, and will reset its
     * SpawnPoint (if any)
     * @param elapsed a double specifying the number
     * of milliseconds since update was last called
     */
    public virtual void update(double elapsed)
    {
        Vector3 screenOrigin = Camera.worldToScreen(origin);
        Vector2 flatOrigin = new Vector2(screenOrigin.X, screenOrigin.Y);
        Vector2 screenMax = flatOrigin + myType.cullMaximumOffset;
        Vector2 screenMin = flatOrigin + myType.cullMinimumOffset;
        //make sure I am still onscreen
        //if not, set cull=false and reset() myType
        if (!Camera.isOnScreen(screenMin, screenMax))
        {
            cull = true;
            if (spawner != null)
            {
                spawner.reset();
            }
        }
    }

    /**
     * stub in case we need to do debug
     * drawing later.
     */
    public virtual void draw()
    {
        if (debug)
        {
            Texture2D tex = Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.OUTLINE).get("outline").spriteSheet;
            Vector3 screenMax = Camera.worldToScreen(max);
            Vector2 flatMax = new Vector2(screenMax.X, screenMax.Y);
            Vector3 screenMin = Camera.worldToScreen(new Vector3(min.X, min.Y, max.Z));
            Vector2 flatMin = new Vector2(screenMin.X, screenMin.Y);
            Game.getGame().getSpriteBatch().Draw(
                tex, // texture
                flatMax, // position
                Color.White // color
                );
            Game.getGame().getSpriteBatch().Draw(
                tex, // texture
                flatMin, // position
                Color.White // color
                );
            Vector2 temp = new Vector2(flatMax.X, flatMin.Y);
            Game.getGame().getSpriteBatch().Draw(
                tex, // texture
                temp, // position
                Color.White // color
                );
            temp = new Vector2(flatMin.X, flatMax.Y);
            Game.getGame().getSpriteBatch().Draw(
                tex, // texture
                temp, // position
                Color.White // color
                );
            screenMax = Camera.worldToScreen(new Vector3(max.X, max.Y, min.Z));
            flatMax = new Vector2(screenMax.X, screenMax.Y);
            screenMin = Camera.worldToScreen(min);
            flatMin = new Vector2(screenMin.X, screenMin.Y);
            Game.getGame().getSpriteBatch().Draw(
                tex, // texture
                flatMax, // position
                Color.Black // color
                );
            Game.getGame().getSpriteBatch().Draw(
                tex, // texture
                flatMin, // position
                Color.Black // color
                );
            temp = new Vector2(flatMax.X, flatMin.Y);
            Game.getGame().getSpriteBatch().Draw(
                tex, // texture
                temp, // position
                Color.Black // color
                );
            temp = new Vector2(flatMin.X, flatMax.Y);
            Game.getGame().getSpriteBatch().Draw(
                tex, // texture
                temp, // position
                Color.Black // color
                );
            /* for cull boundary visualization
            screenMax = Camera.worldToScreen(origin);
            flatMax = new Vector2(screenMax.X, screenMax.Y)+thisType.cullMaximumOffset;
            flatMin = new Vector2(screenMax.X, screenMax.Y)+thisType.cullMinimumOffset;
            Game.getGame().getSpriteBatch().Draw(
                tex, // texture
                flatMax, // position
                Color.Magenta // color
                );
            Game.getGame().getSpriteBatch().Draw(
                tex, // texture
                flatMin, // position
                Color.Magenta // color
                );
             */
        }
    }

    public class Type
    {
        //actual class data
        /**
         * read-only. the offset from the origin
         * to the minimum x,y position of the 
         * on-screen bounding box that must
         * be beyond the camera before this
         * object can be culled.
         */
        public Vector2 cullMinimumOffset
        {
            get { return cullMinOffset; }
        }
        protected Vector2 cullMinOffset;
        /**
         * read-only. the offset from the origin
         * to the maximum x,y position of the 
         * on-screen bounding box that must
         * be beyond the camera before this
         * object can be culled.
         */
        public Vector2 cullMaximumOffset
        {
            get { return cullMaxOffset; }
        }
        protected Vector2 cullMaxOffset;
        /**
         * read-only, origin offset.
         * a Vector3 representing the offset from 
         * the top, front, left corner to the
         * origin of this object 
         * (bottom, front, center)
         */
        public Vector3 collisionBoxOriginOffset
        {
            get { return collisionOriginOffset; }
        }
        protected Vector3 collisionOriginOffset;
        /**
         * read-only, collision cube dimensions.
         * a Vector3 representing the offset from
         * the top, front, left corner to the
         * bottom, back, right corner.
         */
        public Vector3 collisionBoxDimentions
        {
            get { return collisionDimentions; }
        }
        protected Vector3 collisionDimentions;
        /**
         * read-only, enum representing this type.
         */
        public ObjectFactory.TypeEnum myType
        {
            get { return thisType; }
        }
        protected ObjectFactory.TypeEnum thisType;
        
        /**
         * dummy constructor. 
         * used during development.
         */
        protected Type() { }

        /**
         * initialized all the variables.
         * @param type the enum to define this type
         * @param origin the origin's offset from
         * top, front, left.
         * @param collision the vector going from
         * the top, front, left corner to the
         * bottom, back, right corner.
         */
        protected Type(ObjectFactory.TypeEnum type, Vector3 origin, Vector3 collision)
        {
            thisType = type;
            collisionOriginOffset = origin;
            collisionDimentions = collision;
            Vector3 worldReference = Camera.worldToScreen(new Vector3(-collisionOriginOffset.X, -collisionOriginOffset.Y, collisionDimentions.Z));
            Vector3 worldCollision = Camera.worldToScreen(new Vector3(collisionDimentions.X-collisionOriginOffset.X, collisionDimentions.Y-collisionOriginOffset.Y, 0));
            cullMinOffset = new Vector2(worldReference.X, worldReference.Y);
            cullMaxOffset = new Vector2(worldCollision.X, worldCollision.Y);
        }
    }
}
